dbtからSnowflakeのダイナミックデータマスキングを設定できるpackage「dbt_snow_mask」を試してみた #dbt #SnowflakeDB
さがらです。
dbtにはたくさんのpackageが用意されており、Snowflake専用のpackageもいくつかあります。
その中からdbt_snow_maskを試してみたので、内容をまとめてみたいと思います。
dbt_snow_maskとは
dbt_snow_maskに関する情報は、下記ページにまとまっております。
このページの説明を見ると、
This dbt package contains macros that can be (re)used across dbt projects with snowflake. dbt_snow_mask will help to apply Dynamic Data Masking using dbt meta.
…ということで、Snowflakeのダイナミックデータマスキングの設定に役立つpackageのようです。
ダイナミックデータマスキングとは
ここで、ダイナミックデータマスキングとは何か、説明しておきます。
簡単に言うと、Snowflakeにおける列レベルのセキュリティです。
より具体的には、あるカラムに対して「どのロールを持つ人にはこのカラムを値を見せる」のようなマスキングポリシーを定義することで、ユーザーに付与したロールごとにそのカラムの値を見せる・見せないを動的に切り替えることが出来ます。
以下、具体例を載せます。
下記のマスキングポリシーを適用することを考えてみます。内容としては、現在のロールが「SAGARA_ADMIN_ROLE」であれば値はそのまま見せるけど、このロールでなければ値は見せない、という内容です。
CREATE MASKING POLICY IF NOT EXISTS sagara_dbt_test_db.dbt_ssagara.first_name_mask AS (val string) RETURNS string -> CASE WHEN CURRENT_ROLE() IN ('SAGARA_ADMIN_ROLE') THEN val ELSE '**********' END
これをFIRST_NAME
というカラムに対して、適用してみます。
alter table sagara_dbt_test_db.dbt_ssagara.customers modify column first_name set masking policy sagara_dbt_test_db.dbt_ssagara.first_name_mask;
この上で、各ロールごとにどうなるかを見てみます。
- 現在のロールが「SAGARA_ADMIN_ROLE」の場合
- 現在のロールが「SAGARA_ADMIN_ROLE」以外の場合
このように、ロールによって見せる値を動的に変更できるのが、Snowflakeのダイナミックデータマスキングです。
また、ダイナミックデータマスキングの公式Docはこちらになりますので、併せてご覧ください。
dbt_snow_maskを用いたマスキングポリシーの定義と適用
実際に、dbt_snow_maskを試してみます。内容としては、先程ダイナミックデータマスキングの例として見せた内容を、dbt_snow_maskを通じて設定してみます。
dbt_snow_maskのインストール
まず、dbt_snow_maskを対象のdbtプロジェクトに対してインストールします。
dbt_project.yml
と同じ階層に、packages.yml
を定義し、以下の内容を記述します。すでにpackages.yml
がある場合は、下記の内容の- package:
以降を追記してください。
※2022年6月6日時点ではv0.2.0となっていますが、随時適用させたいバージョンは確認をお願い致します。
注意点としては、dbt_snow_maskはdbt_utilsがインストールされていないと使用できません。dbt_utilsも忘れずにインストールしましょう。
packages: - package: dbt-labs/dbt_utils version: 0.8.5 - package: entechlog/dbt_snow_mask version: 0.2.0
後はpackageのお作法に沿って、dbt deps
コマンドを実行すればdbt_snow_maskをインストール出来ます。
マスキングポリシーの作成
このpackageを使用することにより、dbtからマスキングポリシーを新しく作成することが出来ます。
ここでは、macroを新しく定義します。この時の注意点としては、「作成するmacroファイルの名称」「定義するmacroの名称」「このmacroにより作成するマスキングポリシーの名称」の3つがルールに沿っていないと、後でマスキングポリシーを適用させるときに上手くいきません。
- 作成するmacroファイルの名称:
- create_masking_policy_”マスキングポリシー名”.sql
- 定義するmacroの名称:
- create_masking_policy_”マスキングポリシー名”(node_database,node_schema)
- ※末尾の()はmacroの引数を意味しています。node_databaseとnode_schemaの2つの引数が必要です。
- このmacroにより作成するマスキングポリシーの名称:
- {{node_database}}.{{node_schema}}.”マスキングポリシー名”
- ※先頭のJinjaによる記述は、動的なデータベース・スキーマ指定のために必要です。
具体的には、下記のようにコードを記述して、マスキングポリシー生成のための新しいmacroを作成します。
-- ファイル名:create_masking_policy_first_name_mask.sql {% macro create_masking_policy_first_name_mask(node_database,node_schema) %} CREATE MASKING POLICY IF NOT EXISTS {{node_database}}.{{node_schema}}.first_name_mask AS (val string) RETURNS string -> CASE WHEN CURRENT_ROLE() IN ('SAGARA_ADMIN_ROLE') THEN val ELSE '**********' END {% endmacro %}
下図は実際のdbt Cloud上の画像ですが、画像の赤枠内が「マスキングポリシー名」です。
作成するマスキングポリシーをどのカラムに適用するか定義
続いて、作成するマスキングポリシーをどのカラムに適用するか定義します。
具体的には、models
に関するyamlファイルの中で、下記のように記述します。
meta:
欄がポイントで、masking_policy
に先程作成したマスキングポリシー名を記述している形となります。
version: 2 models: - name: customers columns: - name: first_name meta: masking_policy: first_name_mask
もちろん、他のdescription
やtests
と併記することは可能です。下図は実際に私が試した時のyamlファイルの中身です。
macroをmodelに適用する
次に、作成したmacroをmodelに適用します。
私が上手くいった方法は、「modelファイルに対してconfigでpre_hookとpost_hookを設定し、dbt run実行時にマスキングポリシーの作成と適用を行う」方法です。 (コマンドラインで適用する方法もあるのですが、私の場合はapplyが上手く実行されず…原因は不明です。)
具体的には、modelファイルに下記のように記述します。
{{ config( pre_hook=[ "{{ dbt_snow_mask.create_masking_policy('models')}}" ], post_hook=[ "{{ dbt_snow_mask.apply_masking_policy('models') }}" ] ) }} -- 以下、modelに記述したselect文が続きます
dbt runを実行してマスキングポリシーを作成&適用
設定としてはここまでで完了です!
ここでdbt run
を実行すると、このようなクエリ履歴となります。hookを指定しているため、対象のmodelの実行前後でマスキングポリシーの作成・適用が行われているのがわかると思います。
実際に、マスキングポリシーで指定したロール以外から参照してみると、マスキングが適用されていることがわかります!これで、dbtからSnowflakeへのダイナミックデータマスキングの適用が無事にできました。
上手くいかなかったこと:マスキングポリシーの作成先のデータベースやスキーマを分ける
packageの説明のHow to configure database and schema for the masking policy ?を見ると、共有のデータベースやスキーマを新しく作成し、その中にマスキングポリシーを定義する、ということも出来るようです。
しかし、dbt_project.yml
に下記のように追記した上でdbt run
を実行しても、対象のスキーマが自動で作られず、マスキングポリシーを作成するタイミングでエラーとなってしまいました。
vars: use_common_masking_policy_schema_only: "True" common_masking_policy_schema: "masking_policies_schema" create_masking_policy_schema: "True"
これの原因はよくわからなかったです…ちょっと心残りですね。
ちなみにこの設定を行わない場合は、通常の開発プロセスと同じスキーマにマスキングポリシーが定義されます。
- 開発環境:dbt_ssagaraのような開発用スキーマ
- 本番環境:Environmetで指定したスキーマ
私が試したときも、このスキーマ構成に沿ってマスキングポリシーが作成され、対象のテーブルのカラムに適用されていきました。
最後に
dbtからSnowflakeのダイナミックデータマスキングを適用する、dbt_snow_maskを試してみました。
少し上手くいかない所はあったものの、dbtだけでマスキングポリシーを適用できる、というのは面白いなーと感じました!
Snowflakeは他にもdbt packageがあるので、試していきたいですね。